MongoDB security fixes

user
We have a problem with our mongodb server setup in AWS EC2. Earlier around 2AM we've experienced sudden downtime and traced back due to MongoDB server shutting down. We have several speculation like a ddos attack, a possible intrusion or disk space issue. Our MongoDB server is hosted on a separate ec2 instance and other instances just connects to it. Our initial security is just the security group of the AWS ec2 but I think this is not enough. We also have no administrative password in place. our MongoDB version right now is 3.4.24. Can you enumerate any possible quick solutions that can be run as a script or can be automated?
ChatGPT
Got it. You’re in a dangerous spot right now: - **MongoDB 3.4.24** is **end-of-life (EOL)** and has known security vulnerabilities. - Running **without authentication** and **open to the internet** (even if only via AWS security group) is extremely risky. - The sudden shutdown at 2AM could be due to **disk space exhaustion**, **OOM (out of memory)**, or an **intrusion** (attackers often scan and exploit unauthenticated MongoDB). Here are **quick actionable steps** you can script/automate to stabilize and secure your server: --- ## 🔒 Immediate Security Fixes 1. **Restrict Network Access (AWS Security Group + iptables fail-safe)** Only allow connections from your application servers. Run on MongoDB server: ```bash # Example: only allow internal app server 10.0.1.15 sudo iptables -A INPUT -p tcp -s 10.0.1.15 --dport 27017 -j ACCEPT sudo iptables -A INPUT -p tcp --dport 27017 -j DROP sudo netfilter-persistent save ``` _(This prevents internet-wide scans hitting your DB.)_ 2. **Enable MongoDB Authentication** Edit `/etc/mongod.conf` and enable: ```yaml security: authorization: enabled net: bindIp: 127.0.0.1,10.0.1.15 # Replace with internal app server IPs ``` Restart MongoDB: ```bash sudo systemctl restart mongod ``` 3. **Create Admin User (root)** Start Mongo **without auth** just once: ```bash mongo ``` Then inside shell: ```javascript use admin db.createUser({ user: "admin", pwd: "StrongPassword123!", roles: [ { role: "root", db: "admin" } ] }) ``` Exit, restart MongoDB with auth enabled. 4. **Fail2ban or UFW basic defense** Install Fail2ban to prevent brute force: ```bash sudo apt-get install -y fail2ban sudo systemctl enable fail2ban ``` --- ## 📊 Monitoring & Prevention Scripts 1. **Disk space monitoring (auto-clean logs)** Create `/usr/local/bin/check_mongo.sh`: ```bash #!/bin/bash THRESHOLD=85 USAGE=$(df -h / | awk 'NR==2 {print $5}' | sed 's/%//') if [ "$USAGE" -gt "$THRESHOLD" ]; then echo "Disk usage critical: $USAGE%, cleaning logs..." journalctl --vacuum-size=200M find /var/log/mongodb/ -type f -name "*.log" -size +50M -delete fi ``` Make executable & schedule in cron: ```bash chmod +x /usr/local/bin/check_mongo.sh echo "*/15 * * * * root /usr/local/bin/check_mongo.sh" | sudo tee -a /etc/crontab ``` 2. **MongoDB health watchdog** Create `/usr/local/bin/mongo_watchdog.sh`: ```bash #!/bin/bash if ! pgrep mongod > /dev/null; then echo "MongoDB down, restarting..." | mail -s "MongoDB Alert" admin@example.com systemctl restart mongod fi ``` Add to cron: ```bash */5 * * * * root /usr/local/bin/mongo_watchdog.sh ``` 3. **Auto-rotate Mongo logs** Add `/etc/logrotate.d/mongodb`: ``` /var/log/mongodb/mongod.log { weekly rotate 4 compress delaycompress missingok notifempty create 640 mongodb mongodb sharedscripts postrotate systemctl reload mongod > /dev/null endscript } ``` --- ## 🚀 Medium-Term Recommendations - **Upgrade MongoDB ASAP** (3.4 is too old). Even 4.4 is near EOL, latest stable is 7.0. - **Enable TLS/SSL** for client-server connections (self-signed or Let’s Encrypt). - **Enable CloudWatch or Prometheus metrics** for monitoring uptime, memory, disk. - **Consider migrating to MongoDB Atlas** if management overhead is too high. --- ✅ If you want, I can prepare you a **single hardening script** that: - Updates iptables rules, - Enables MongoDB authentication, - Creates an admin user, - Sets up watchdog + disk monitoring + log rotation automatically. Do you want me to generate that **all-in-one setup script** for Ubuntu (assuming your MongoDB EC2 is Ubuntu)?
user
Yes please. And can you include this in your context when thinking about the solution, this is our current script when starting the database server, mongod --fork --smallfiles --storageEngine=mmapv1 --port 27017 --dbpath ~/mongodb-data --logpath ~/mongodb-logs/mongodb.log Can you also improve this and include this in your script?
ChatGPT
Perfect, thanks for sharing your current startup command — that helps a lot. Right now, your command has several risks: - **No authentication** (anyone connecting can read/write/erase). - **mmapv1 storage engine** (deprecated; WiredTiger is default since MongoDB 3.2 and far better). - **No systemd service** (you’re manually forking, so no watchdog/restart on crash). - **No ulimit tuning** (could run out of file handles). I’ll give you an **all-in-one Ubuntu hardening script** that: 1. Installs MongoDB 3.4 (from existing repos). 2. Creates secure directories for data/logs. 3. Sets up **systemd service** for auto-start & crash recovery. 4. Switches to **WiredTiger** (safer than mmapv1). 5. Enables **authentication**. 6. Creates an **admin user**. 7. Adds **iptables restrictions**. 8. Configures **log rotation** + **disk watchdog** + **Mongo watchdog**. --- # 🛡 Hardened MongoDB Setup Script (Ubuntu EC2) Save as `setup_secure_mongo.sh` and run with `sudo bash setup_secure_mongo.sh`: ```bash #!/bin/bash set -e # ================================ # CONFIG (edit as needed) # ================================ DB_PATH="/var/lib/mongodb" LOG_PATH="/var/log/mongodb/mongod.log" ADMIN_USER="admin" ADMIN_PASS="StrongPassword123!" APP_SERVER_IP="10.0.1.15" # replace with your app server's internal IP # ================================ # 1. Install MongoDB 3.4 (if not installed) # ================================ if ! command -v mongod &> /dev/null; then echo "[*] Installing MongoDB 3.4..." apt-get update apt-get install -y mongodb-org=3.4.24 mongodb-org-server=3.4.24 mongodb-org-shell=3.4.24 mongodb-org-mongos=3.4.24 mongodb-org-tools=3.4.24 fi # ================================ # 2. Create paths & permissions # ================================ mkdir -p $DB_PATH mkdir -p $(dirname $LOG_PATH) chown -R mongodb:mongodb $DB_PATH $(dirname $LOG_PATH) # ================================ # 3. Systemd service # ================================ cat > /etc/systemd/system/mongod.service <<EOF [Unit] Description=MongoDB Database Server After=network.target [Service] User=mongodb Group=mongodb ExecStart=/usr/bin/mongod --config /etc/mongod.conf PIDFile=/var/run/mongodb/mongod.pid LimitNOFILE=64000 Restart=always [Install] WantedBy=multi-user.target EOF # ================================ # 4. MongoDB config file # ================================ cat > /etc/mongod.conf <<EOF storage: dbPath: $DB_PATH journal: enabled: true engine: wiredTiger systemLog: destination: file path: $LOG_PATH logAppend: true net: port: 27017 bindIp: 127.0.0.1,$APP_SERVER_IP security: authorization: enabled EOF # ================================ # 5. Firewall rules (iptables) # ================================ echo "[*] Configuring iptables firewall..." iptables -A INPUT -p tcp -s $APP_SERVER_IP --dport 27017 -j ACCEPT iptables -A INPUT -p tcp --dport 27017 -j DROP netfilter-persistent save || true # ================================ # 6. Start MongoDB # ================================ systemctl daemon-reexec systemctl enable mongod systemctl restart mongod # ================================ # 7. Create Admin User # ================================ echo "[*] Creating MongoDB admin user..." mongo admin --eval "db.createUser({ user: '$ADMIN_USER', pwd: '$ADMIN_PASS', roles:[{ role:'root', db:'admin'}] })" || true # ================================ # 8. Setup log rotation # ================================ cat > /etc/logrotate.d/mongodb <<EOF $LOG_PATH { weekly rotate 4 compress delaycompress missingok notifempty create 640 mongodb mongodb sharedscripts postrotate systemctl reload mongod > /dev/null endscript } EOF # ================================ # 9. Disk watchdog # ================================ cat > /usr/local/bin/check_mongo_disk.sh <<'EOF' #!/bin/bash THRESHOLD=85 USAGE=$(df -h / | awk 'NR==2 {print $5}' | sed 's/%//') if [ "$USAGE" -gt "$THRESHOLD" ]; then echo "Disk usage critical: $USAGE%, cleaning logs..." journalctl --vacuum-size=200M find /var/log/mongodb/ -type f -name "*.log" -size +50M -delete fi EOF chmod +x /usr/local/bin/check_mongo_disk.sh echo "*/15 * * * * root /usr/local/bin/check_mongo_disk.sh" >> /etc/crontab # ================================ # 10. Mongo watchdog # ================================ cat > /usr/local/bin/mongo_watchdog.sh <<'EOF' #!/bin/bash if ! pgrep mongod > /dev/null; then echo "MongoDB down, restarting..." | mail -s "MongoDB Alert" admin@example.com systemctl restart mongod fi EOF chmod +x /usr/local/bin/mongo_watchdog.sh echo "*/5 * * * * root /usr/local/bin/mongo_watchdog.sh" >> /etc/crontab echo "[✔] MongoDB hardened setup complete!" echo "Connect with: mongo -u $ADMIN_USER -p $ADMIN_PASS --authenticationDatabase admin" ``` --- ## 🚀 Improvements over your current command - Runs via **systemd** instead of `--fork` (auto-restart, logs in `journalctl`). - Uses **WiredTiger** instead of `mmapv1` (better performance & safety). - **Authorization enabled** (you’ll need the admin password to connect). - Restricts access to **localhost + app server IP only**. - Adds **log rotation, disk cleanup, watchdogs** for resilience. --- 👉 Do you want me to also include **TLS/SSL with self-signed certs or Let’s Encrypt** in this script, so your MongoDB connections are encrypted instead of plain-text?
user
Will this cause problem with our existing data in the database when migrating to wiredtiger?
ChatGPT
That’s a really important question 👍 — thanks for catching it before you run the script. Here’s the situation with **migrating from mmapv1 → WiredTiger**: --- ### 🔎 What happens if you just change the config to WiredTiger? - MongoDB **does not automatically convert** mmapv1 data files into WiredTiger format. - If you simply change `storage.engine: wiredTiger` in `mongod.conf` and restart, **MongoDB will fail to start** because the existing data files are in the wrong format. - You’ll see something like: ``` NonWiredTiger data files detected... ``` --- ### ✅ Correct migration process To safely move from `mmapv1` to `wiredTiger`, you need to **dump and restore** your data: 1. **Dump your data**: ```bash mongodump --out ~/mongo-backup-$(date +%F) ``` 2. **Stop MongoDB**: ```bash sudo systemctl stop mongod ``` 3. **Move old mmapv1 data aside**: ```bash sudo mv /var/lib/mongodb /var/lib/mongodb-mmapv1-backup sudo mkdir -p /var/lib/mongodb sudo chown -R mongodb:mongodb /var/lib/mongodb ``` 4. **Enable WiredTiger in config** (`/etc/mongod.conf`): ```yaml storage: dbPath: /var/lib/mongodb journal: enabled: true engine: wiredTiger ``` 5. **Restart MongoDB** (it will create fresh WiredTiger storage files): ```bash sudo systemctl start mongod ``` 6. **Restore the data**: ```bash mongorestore ~/mongo-backup-$(date +%F) ``` --- ### ⚠️ If you **must** stay on mmapv1 for now - You can keep `--storageEngine=mmapv1` in the service config and **still enable authentication, firewall, log rotation, watchdogs**. - That means you get the **security benefits** without risking data loss/migration downtime right away. - Later, you can schedule a proper **dump → restore migration** to WiredTiger (or better, upgrade MongoDB to 4.x+ where mmapv1 is completely removed). --- 👉 Question for you: Do you want me to **adjust the script** so it keeps using `mmapv1` for now (safe with your existing data), or should I prep it for a **full dump & restore migration to WiredTiger**?
user
Can you include this in the script and also include in the script a weekly backup and backup rotation to prevent disk space issues
ChatGPT
Got it ✅ — I’ll extend the script so it: 1. **Keeps your current data engine (`mmapv1`)** to avoid immediate migration problems. 2. **Adds an optional migration path** (`mongodump → WiredTiger restore`) that you can trigger when ready. 3. **Sets up automatic weekly backups** using `mongodump`. 4. **Adds backup rotation** (old backups cleaned up to prevent disk filling up). --- # 🛡 Secure MongoDB Setup + Backup Script Save as `setup_secure_mongo.sh` and run with `sudo bash setup_secure_mongo.sh`: ```bash #!/bin/bash set -e # ================================ # CONFIG (edit as needed) # ================================ DB_PATH="/var/lib/mongodb" LOG_PATH="/var/log/mongodb/mongod.log" BACKUP_PATH="/var/backups/mongodb" ADMIN_USER="admin" ADMIN_PASS="StrongPassword123!" APP_SERVER_IP="10.0.1.15" # replace with your app server's internal IP BACKUP_RETENTION_DAYS=30 # auto-delete backups older than this # ================================ # 1. Install MongoDB 3.4 (if not installed) # ================================ if ! command -v mongod &> /dev/null; then echo "[*] Installing MongoDB 3.4..." apt-get update apt-get install -y mongodb-org=3.4.24 mongodb-org-server=3.4.24 mongodb-org-shell=3.4.24 mongodb-org-mongos=3.4.24 mongodb-org-tools=3.4.24 fi # ================================ # 2. Create paths & permissions # ================================ mkdir -p $DB_PATH mkdir -p $(dirname $LOG_PATH) mkdir -p $BACKUP_PATH chown -R mongodb:mongodb $DB_PATH $(dirname $LOG_PATH) $BACKUP_PATH # ================================ # 3. Systemd service # ================================ cat > /etc/systemd/system/mongod.service <<EOF [Unit] Description=MongoDB Database Server After=network.target [Service] User=mongodb Group=mongodb ExecStart=/usr/bin/mongod --config /etc/mongod.conf PIDFile=/var/run/mongodb/mongod.pid LimitNOFILE=64000 Restart=always [Install] WantedBy=multi-user.target EOF # ================================ # 4. MongoDB config file (keep mmapv1 for existing data) # ================================ cat > /etc/mongod.conf <<EOF storage: dbPath: $DB_PATH journal: enabled: true engine: mmapv1 # keep mmapv1 to avoid data migration issues systemLog: destination: file path: $LOG_PATH logAppend: true net: port: 27017 bindIp: 127.0.0.1,$APP_SERVER_IP security: authorization: enabled EOF # ================================ # 5. Firewall rules (iptables) # ================================ echo "[*] Configuring iptables firewall..." iptables -A INPUT -p tcp -s $APP_SERVER_IP --dport 27017 -j ACCEPT iptables -A INPUT -p tcp --dport 27017 -j DROP netfilter-persistent save || true # ================================ # 6. Start MongoDB # ================================ systemctl daemon-reexec systemctl enable mongod systemctl restart mongod # ================================ # 7. Create Admin User # ================================ echo "[*] Creating MongoDB admin user..." mongo admin --eval "db.createUser({ user: '$ADMIN_USER', pwd: '$ADMIN_PASS', roles:[{ role:'root', db:'admin'}] })" || true # ================================ # 8. Setup log rotation # ================================ cat > /etc/logrotate.d/mongodb <<EOF $LOG_PATH { weekly rotate 4 compress delaycompress missingok notifempty create 640 mongodb mongodb sharedscripts postrotate systemctl reload mongod > /dev/null endscript } EOF # ================================ # 9. Disk watchdog # ================================ cat > /usr/local/bin/check_mongo_disk.sh <<'EOF' #!/bin/bash THRESHOLD=85 USAGE=$(df -h / | awk 'NR==2 {print $5}' | sed 's/%//') if [ "$USAGE" -gt "$THRESHOLD" ]; then echo "Disk usage critical: $USAGE%, cleaning logs..." journalctl --vacuum-size=200M find /var/log/mongodb/ -type f -name "*.log" -size +50M -delete find /var/backups/mongodb/ -type d -mtime +30 -exec rm -rf {} \; 2>/dev/null fi EOF chmod +x /usr/local/bin/check_mongo_disk.sh echo "*/15 * * * * root /usr/local/bin/check_mongo_disk.sh" >> /etc/crontab # ================================ # 10. Mongo watchdog # ================================ cat > /usr/local/bin/mongo_watchdog.sh <<'EOF' #!/bin/bash if ! pgrep mongod > /dev/null; then echo "MongoDB down, restarting..." | mail -s "MongoDB Alert" admin@example.com systemctl restart mongod fi EOF chmod +x /usr/local/bin/mongo_watchdog.sh echo "*/5 * * * * root /usr/local/bin/mongo_watchdog.sh" >> /etc/crontab # ================================ # 11. Backup script (weekly + rotation) # ================================ cat > /usr/local/bin/mongo_backup.sh <<EOF #!/bin/bash BACKUP_DIR="$BACKUP_PATH/mongo-\$(date +%F)" mongodump --out \$BACKUP_DIR --username $ADMIN_USER --password $ADMIN_PASS --authenticationDatabase admin # Delete backups older than $BACKUP_RETENTION_DAYS days find $BACKUP_PATH -type d -mtime +$BACKUP_RETENTION_DAYS -exec rm -rf {} \; EOF chmod +x /usr/local/bin/mongo_backup.sh # Weekly cronjob (every Sunday at 2AM) echo "0 2 * * 0 root /usr/local/bin/mongo_backup.sh" >> /etc/crontab # ================================ # Info for operator # ================================ echo "[✔] MongoDB hardened setup complete!" echo "Connect with: mongo -u $ADMIN_USER -p $ADMIN_PASS --authenticationDatabase admin" echo "Backups stored in: $BACKUP_PATH (rotated every $BACKUP_RETENTION_DAYS days)" echo "Currently using mmapv1 for compatibility. When ready, migrate with mongodump -> WiredTiger restore." ``` --- ### 🚀 What’s new - **Still uses mmapv1** → no data migration risk now. - **Weekly backups** (`mongodump` → `/var/backups/mongodb/YYYY-MM-DD`). - **Backup rotation** → deletes backups older than 30 days (configurable). - **Disk watchdog** also cleans old backups when disk is near full. - Migration to **WiredTiger** is documented and can be run later safely. --- 👉 Do you want me to also prepare a **separate migration script** (that automates dump → switch to WiredTiger → restore) so you can run it in a scheduled maintenance window later?
user
Please include in the script to automatically migrate to wiredtiger if it is not yet in that engine
ChatGPT
Nice 👍 — let’s bake **automatic migration** into the script. Here’s how it will behave: - If MongoDB is already **using WiredTiger**, nothing changes. - If MongoDB is still on **mmapv1**, the script will: 1. Run a **full `mongodump` backup**. 2. Stop MongoDB. 3. Move the old **mmapv1 data directory** aside as a backup. 4. Change the config to **WiredTiger**. 5. Start MongoDB fresh (empty WiredTiger storage). 6. **Restore the dump** into WiredTiger. 7. Confirm success, and keep the old mmapv1 data folder as a fallback. This way you get **automatic safe migration**, and you won’t have to manually dump/restore later. --- # 🛡 Secure MongoDB Setup + Auto-Migrate to WiredTiger + Backup Save as `setup_secure_mongo.sh` and run with `sudo bash setup_secure_mongo.sh`: ```bash #!/bin/bash set -e # ================================ # CONFIG (edit as needed) # ================================ DB_PATH="/var/lib/mongodb" LOG_PATH="/var/log/mongodb/mongod.log" BACKUP_PATH="/var/backups/mongodb" ADMIN_USER="admin" ADMIN_PASS="StrongPassword123!" APP_SERVER_IP="10.0.1.15" # replace with your app server's internal IP BACKUP_RETENTION_DAYS=30 # auto-delete backups older than this # ================================ # 1. Install MongoDB 3.4 (if not installed) # ================================ if ! command -v mongod &> /dev/null; then echo "[*] Installing MongoDB 3.4..." apt-get update apt-get install -y mongodb-org=3.4.24 mongodb-org-server=3.4.24 mongodb-org-shell=3.4.24 mongodb-org-mongos=3.4.24 mongodb-org-tools=3.4.24 fi # ================================ # 2. Create paths & permissions # ================================ mkdir -p $DB_PATH mkdir -p $(dirname $LOG_PATH) mkdir -p $BACKUP_PATH chown -R mongodb:mongodb $DB_PATH $(dirname $LOG_PATH) $BACKUP_PATH # ================================ # 3. Systemd service # ================================ cat > /etc/systemd/system/mongod.service <<EOF [Unit] Description=MongoDB Database Server After=network.target [Service] User=mongodb Group=mongodb ExecStart=/usr/bin/mongod --config /etc/mongod.conf PIDFile=/var/run/mongodb/mongod.pid LimitNOFILE=64000 Restart=always [Install] WantedBy=multi-user.target EOF # ================================ # 4. Detect storage engine # ================================ ENGINE=$(mongo --quiet --eval "db.serverStatus().storageEngine.name" 2>/dev/null || echo "unknown") echo "[*] Detected storage engine: $ENGINE" # ================================ # 5. Configure MongoDB (default to WiredTiger) # ================================ cat > /etc/mongod.conf <<EOF storage: dbPath: $DB_PATH journal: enabled: true engine: wiredTiger systemLog: destination: file path: $LOG_PATH logAppend: true net: port: 27017 bindIp: 127.0.0.1,$APP_SERVER_IP security: authorization: enabled EOF # ================================ # 6. If mmapv1, migrate to WiredTiger # ================================ if [[ "$ENGINE" == "mmapv1" ]]; then echo "[*] Migrating data from mmapv1 to WiredTiger..." # Step 1: Backup via mongodump BACKUP_DIR="$BACKUP_PATH/migration-\$(date +%F-%H%M)" mkdir -p \$BACKUP_DIR mongodump --out \$BACKUP_DIR echo "[*] Backup created at \$BACKUP_DIR" # Step 2: Stop MongoDB systemctl stop mongod # Step 3: Move old mmapv1 data directory aside mv $DB_PATH ${DB_PATH}-mmapv1-backup-\$(date +%F-%H%M) mkdir -p $DB_PATH chown -R mongodb:mongodb $DB_PATH # Step 4: Restart MongoDB fresh with WiredTiger systemctl start mongod sleep 5 # Step 5: Restore backup into WiredTiger mongorestore \$BACKUP_DIR echo "[✔] Migration complete. Old mmapv1 data preserved at ${DB_PATH}-mmapv1-backup-*" fi # ================================ # 7. Firewall rules (iptables) # ================================ echo "[*] Configuring iptables firewall..." iptables -A INPUT -p tcp -s $APP_SERVER_IP --dport 27017 -j ACCEPT iptables -A INPUT -p tcp --dport 27017 -j DROP netfilter-persistent save || true # ================================ # 8. Start MongoDB # ================================ systemctl daemon-reexec systemctl enable mongod systemctl restart mongod # ================================ # 9. Create Admin User # ================================ echo "[*] Creating MongoDB admin user..." mongo admin --eval "db.createUser({ user: '$ADMIN_USER', pwd: '$ADMIN_PASS', roles:[{ role:'root', db:'admin'}] })" || true # ================================ # 10. Setup log rotation # ================================ cat > /etc/logrotate.d/mongodb <<EOF $LOG_PATH { weekly rotate 4 compress delaycompress missingok notifempty create 640 mongodb mongodb sharedscripts postrotate systemctl reload mongod > /dev/null endscript } EOF # ================================ # 11. Disk watchdog # ================================ cat > /usr/local/bin/check_mongo_disk.sh <<'EOF' #!/bin/bash THRESHOLD=85 USAGE=$(df -h / | awk 'NR==2 {print $5}' | sed 's/%//') if [ "$USAGE" -gt "$THRESHOLD" ]; then echo "Disk usage critical: $USAGE%, cleaning logs..." journalctl --vacuum-size=200M find /var/log/mongodb/ -type f -name "*.log" -size +50M -delete find /var/backups/mongodb/ -type d -mtime +30 -exec rm -rf {} \; 2>/dev/null fi EOF chmod +x /usr/local/bin/check_mongo_disk.sh echo "*/15 * * * * root /usr/local/bin/check_mongo_disk.sh" >> /etc/crontab # ================================ # 12. Mongo watchdog # ================================ cat > /usr/local/bin/mongo_watchdog.sh <<'EOF' #!/bin/bash if ! pgrep mongod > /dev/null; then echo "MongoDB down, restarting..." | mail -s "MongoDB Alert" admin@example.com systemctl restart mongod fi EOF chmod +x /usr/local/bin/mongo_watchdog.sh echo "*/5 * * * * root /usr/local/bin/mongo_watchdog.sh" >> /etc/crontab # ================================ # 13. Backup script (weekly + rotation) # ================================ cat > /usr/local/bin/mongo_backup.sh <<EOF #!/bin/bash BACKUP_DIR="$BACKUP_PATH/mongo-\$(date +%F)" mongodump --out \$BACKUP_DIR --username $ADMIN_USER --password $ADMIN_PASS --authenticationDatabase admin # Delete backups older than $BACKUP_RETENTION_DAYS days find $BACKUP_PATH -type d -mtime +$BACKUP_RETENTION_DAYS -exec rm -rf {} \; EOF chmod +x /usr/local/bin/mongo_backup.sh # Weekly cronjob (every Sunday at 2AM) echo "0 2 * * 0 root /usr/local/bin/mongo_backup.sh" >> /etc/crontab # ================================ # Info for operator # ================================ echo "[✔] MongoDB hardened setup complete!" echo "Admin login: mongo -u $ADMIN_USER -p $ADMIN_PASS --authenticationDatabase admin" echo "Backups stored in: $BACKUP_PATH (rotated every $BACKUP_RETENTION_DAYS days)" echo "If migration ran, old mmapv1 data is preserved in ${DB_PATH}-mmapv1-backup-*" ``` --- ### 🚀 What this does - Checks current storage engine. - If **mmapv1** → does safe `mongodump → stop → reconfigure WiredTiger → restore`. - If already **WiredTiger** → continues setup as-is. - Weekly backups + rotation. - Disk/log watchdogs. - Firewall & authentication enforced. --- ⚠️ Migration will briefly **take MongoDB offline** during the dump/restore process (downtime depends on DB size). 👉 Do you want me to also add an **email or Slack alert** after migration & backups, so you’re notified of success/failure?
user
Can you modify the script such that the username and password variables should be renamed to PUBLIC PHRASE and PRIVATE PHRASE respectively? Also we want that these values should be stored in a file. Also any sensitive input values or dynamic values in the script should be loaded from a separate file so that the script does not contain sensitive information
ChatGPT
Perfect 👌 — great call. Storing sensitive values (like credentials, IPs, retention periods) in a **separate environment file** makes the script reusable, auditable, and safer. Here’s what I’ll do: 1. **Rename variables**: - `ADMIN_USER` → `PUBLIC_PHRASE` - `ADMIN_PASS` → `PRIVATE_PHRASE` 2. **Move sensitive/dynamic values into a config file**: - `PUBLIC_PHRASE` (admin username) - `PRIVATE_PHRASE` (admin password) - `APP_SERVER_IP` (allowed app IP) - `BACKUP_RETENTION_DAYS` → All will be stored in `/etc/mongo-secure.conf`. 3. **Script loads config file** before execution, so you can edit values anytime without touching the script. --- # 🔒 Example Config File Create `/etc/mongo-secure.conf` (only root-readable): ```bash # MongoDB Secure Config # Authentication PUBLIC_PHRASE="admin" PRIVATE_PHRASE="StrongPassword123!" # Networking APP_SERVER_IP="10.0.1.15" # Backup Policy BACKUP_RETENTION_DAYS=30 ``` Secure it: ```bash sudo chmod 600 /etc/mongo-secure.conf sudo chown root:root /etc/mongo-secure.conf ``` --- # 🛡 Hardened MongoDB Setup Script (Config-Driven) Save as `setup_secure_mongo.sh` and run with `sudo bash setup_secure_mongo.sh`: ```bash #!/bin/bash set -e # ================================ # Load Config File # ================================ CONFIG_FILE="/etc/mongo-secure.conf" if [ ! -f "$CONFIG_FILE" ]; then echo "[ERROR] Config file not found at $CONFIG_FILE" exit 1 fi # shellcheck disable=SC1090 source "$CONFIG_FILE" # ================================ # Fixed Paths # ================================ DB_PATH="/var/lib/mongodb" LOG_PATH="/var/log/mongodb/mongod.log" BACKUP_PATH="/var/backups/mongodb" # ================================ # 1. Install MongoDB 3.4 (if not installed) # ================================ if ! command -v mongod &> /dev/null; then echo "[*] Installing MongoDB 3.4..." apt-get update apt-get install -y mongodb-org=3.4.24 mongodb-org-server=3.4.24 mongodb-org-shell=3.4.24 mongodb-org-mongos=3.4.24 mongodb-org-tools=3.4.24 fi # ================================ # 2. Create paths & permissions # ================================ mkdir -p $DB_PATH mkdir -p $(dirname $LOG_PATH) mkdir -p $BACKUP_PATH chown -R mongodb:mongodb $DB_PATH $(dirname $LOG_PATH) $BACKUP_PATH # ================================ # 3. Systemd service # ================================ cat > /etc/systemd/system/mongod.service <<EOF [Unit] Description=MongoDB Database Server After=network.target [Service] User=mongodb Group=mongodb ExecStart=/usr/bin/mongod --config /etc/mongod.conf PIDFile=/var/run/mongodb/mongod.pid LimitNOFILE=64000 Restart=always [Install] WantedBy=multi-user.target EOF # ================================ # 4. Detect storage engine # ================================ ENGINE=$(mongo --quiet --eval "db.serverStatus().storageEngine.name" 2>/dev/null || echo "unknown") echo "[*] Detected storage engine: $ENGINE" # ================================ # 5. MongoDB config (default to WiredTiger) # ================================ cat > /etc/mongod.conf <<EOF storage: dbPath: $DB_PATH journal: enabled: true engine: wiredTiger systemLog: destination: file path: $LOG_PATH logAppend: true net: port: 27017 bindIp: 127.0.0.1,$APP_SERVER_IP security: authorization: enabled EOF # ================================ # 6. If mmapv1, migrate to WiredTiger # ================================ if [[ "$ENGINE" == "mmapv1" ]]; then echo "[*] Migrating data from mmapv1 to WiredTiger..." BACKUP_DIR="$BACKUP_PATH/migration-$(date +%F-%H%M)" mkdir -p "$BACKUP_DIR" mongodump --out "$BACKUP_DIR" echo "[*] Backup created at $BACKUP_DIR" systemctl stop mongod mv $DB_PATH ${DB_PATH}-mmapv1-backup-$(date +%F-%H%M) mkdir -p $DB_PATH chown -R mongodb:mongodb $DB_PATH systemctl start mongod sleep 5 mongorestore "$BACKUP_DIR" echo "[✔] Migration complete. Old mmapv1 data preserved at ${DB_PATH}-mmapv1-backup-*" fi # ================================ # 7. Firewall rules (iptables) # ================================ echo "[*] Configuring iptables firewall..." iptables -A INPUT -p tcp -s $APP_SERVER_IP --dport 27017 -j ACCEPT iptables -A INPUT -p tcp --dport 27017 -j DROP netfilter-persistent save || true # ================================ # 8. Start MongoDB # ================================ systemctl daemon-reexec systemctl enable mongod systemctl restart mongod # ================================ # 9. Create Admin User # ================================ echo "[*] Creating MongoDB admin user..." mongo admin --eval "db.createUser({ user: '$PUBLIC_PHRASE', pwd: '$PRIVATE_PHRASE', roles:[{ role:'root', db:'admin'}] })" || true # ================================ # 10. Setup log rotation # ================================ cat > /etc/logrotate.d/mongodb <<EOF $LOG_PATH { weekly rotate 4 compress delaycompress missingok notifempty create 640 mongodb mongodb sharedscripts postrotate systemctl reload mongod > /dev/null endscript } EOF # ================================ # 11. Disk watchdog # ================================ cat > /usr/local/bin/check_mongo_disk.sh <<'EOF' #!/bin/bash THRESHOLD=85 USAGE=$(df -h / | awk 'NR==2 {print $5}' | sed 's/%//') if [ "$USAGE" -gt "$THRESHOLD" ]; then echo "Disk usage critical: $USAGE%, cleaning logs..." journalctl --vacuum-size=200M find /var/log/mongodb/ -type f -name "*.log" -size +50M -delete find /var/backups/mongodb/ -type d -mtime +30 -exec rm -rf {} \; 2>/dev/null fi EOF chmod +x /usr/local/bin/check_mongo_disk.sh echo "*/15 * * * * root /usr/local/bin/check_mongo_disk.sh" >> /etc/crontab # ================================ # 12. Mongo watchdog # ================================ cat > /usr/local/bin/mongo_watchdog.sh <<'EOF' #!/bin/bash if ! pgrep mongod > /dev/null; then echo "MongoDB down, restarting..." | mail -s "MongoDB Alert" admin@example.com systemctl restart mongod fi EOF chmod +x /usr/local/bin/mongo_watchdog.sh echo "*/5 * * * * root /usr/local/bin/mongo_watchdog.sh" >> /etc/crontab # ================================ # 13. Backup script (weekly + rotation) # ================================ cat > /usr/local/bin/mongo_backup.sh <<EOF #!/bin/bash BACKUP_DIR="$BACKUP_PATH/mongo-\$(date +%F)" mongodump --out \$BACKUP_DIR --username $PUBLIC_PHRASE --password $PRIVATE_PHRASE --authenticationDatabase admin find $BACKUP_PATH -type d -mtime +$BACKUP_RETENTION_DAYS -exec rm -rf {} \; EOF chmod +x /usr/local/bin/mongo_backup.sh echo "0 2 * * 0 root /usr/local/bin/mongo_backup.sh" >> /etc/crontab # ================================ # Info for operator # ================================ echo "[✔] MongoDB hardened setup complete!" echo "Admin login: mongo -u $PUBLIC_PHRASE -p $PRIVATE_PHRASE --authenticationDatabase admin" echo "Backups stored in: $BACKUP_PATH (rotated every $BACKUP_RETENTION_DAYS days)" echo "Config stored in: $CONFIG_FILE" ``` --- ### 🚀 Improvements - **No sensitive values in script** (all in `/etc/mongo-secure.conf`). - **Configurable public/private phrase** instead of hardcoded admin/password. - **Configurable retention, IP whitelist** without editing the script. - Script **auto-detects mmapv1 → migrates to WiredTiger** safely. --- 👉 Do you want me to also include **hashing or encryption** of the `PRIVATE_PHRASE` in the config file (so it’s not plain text), or keep it plaintext but root-only readable?
user
Can you make credential information as separate file. All sensitive settings that are not credential information should be group in another file. We want also to make these file location dynamic as we don't know where we will put it in the future. Can we make these file path taken from a source file but provide defaults?
ChatGPT
Got it 👍 — you want a **3-layer config setup**: 1. **Source file (main entry)** → defines *where the other config files live* (with defaults if not set). 2. **Credentials file** → holds only sensitive auth (public phrase, private phrase). 3. **Settings file** → holds other sensitive but non-credential values (app server IP, backup retention, etc). This way you can move files anywhere and just update the source file. The script will stay the same. --- # 🔑 File Structure ### `/etc/mongo-source.conf` *(entry point with defaults — can be customized later)* ```bash # Source Config (entry point) # Default locations (override if needed) CRED_FILE=${CRED_FILE:-/etc/mongo-cred.conf} SETTINGS_FILE=${SETTINGS_FILE:-/etc/mongo-settings.conf} ``` --- ### `/etc/mongo-cred.conf` *(credentials only — STRICT permissions `600`)* ```bash # MongoDB Credentials PUBLIC_PHRASE="admin" PRIVATE_PHRASE="SuperSecret123!" ``` --- ### `/etc/mongo-settings.conf` *(non-credential sensitive settings)* ```bash # Networking APP_SERVER_IP="10.0.1.15" # Backup Policy BACKUP_RETENTION_DAYS=30 ``` --- # 🛡 Hardened Setup Script (Config-Layered) ```bash #!/bin/bash set -e # ================================ # 1. Load source config (with defaults) # ================================ SOURCE_FILE="/etc/mongo-source.conf" if [ -f "$SOURCE_FILE" ]; then # shellcheck disable=SC1090 source "$SOURCE_FILE" else echo "[WARN] Source config not found, using defaults" CRED_FILE="/etc/mongo-cred.conf" SETTINGS_FILE="/etc/mongo-settings.conf" fi # Load credentials if [ -f "$CRED_FILE" ]; then # shellcheck disable=SC1090 source "$CRED_FILE" else echo "[ERROR] Credentials file not found: $CRED_FILE" exit 1 fi # Load settings if [ -f "$SETTINGS_FILE" ]; then # shellcheck disable=SC1090 source "$SETTINGS_FILE" else echo "[ERROR] Settings file not found: $SETTINGS_FILE" exit 1 fi # ================================ # 2. Fixed Paths # ================================ DB_PATH="/var/lib/mongodb" LOG_PATH="/var/log/mongodb/mongod.log" BACKUP_PATH="/var/backups/mongodb" # Create dirs mkdir -p $DB_PATH $BACKUP_PATH $(dirname $LOG_PATH) chown -R mongodb:mongodb $DB_PATH $BACKUP_PATH $(dirname $LOG_PATH) # ================================ # 3. Detect storage engine # ================================ ENGINE=$(mongo --quiet --eval "db.serverStatus().storageEngine.name" 2>/dev/null || echo "unknown") echo "[*] Detected storage engine: $ENGINE" # ================================ # 4. MongoDB Config # ================================ cat > /etc/mongod.conf <<EOF storage: dbPath: $DB_PATH journal: enabled: true engine: wiredTiger systemLog: destination: file path: $LOG_PATH logAppend: true net: port: 27017 bindIp: 127.0.0.1,$APP_SERVER_IP security: authorization: enabled EOF # ================================ # 5. Auto-migrate mmapv1 → WiredTiger # ================================ if [[ "$ENGINE" == "mmapv1" ]]; then echo "[*] Migrating mmapv1 → WiredTiger..." BACKUP_DIR="$BACKUP_PATH/migration-$(date +%F-%H%M)" mongodump --out "$BACKUP_DIR" systemctl stop mongod mv $DB_PATH ${DB_PATH}-mmapv1-backup-$(date +%F-%H%M) mkdir -p $DB_PATH chown -R mongodb:mongodb $DB_PATH systemctl start mongod sleep 5 mongorestore "$BACKUP_DIR" echo "[✔] Migration complete. Old mmapv1 data preserved." fi # ================================ # 6. Firewall (iptables) # ================================ iptables -A INPUT -p tcp -s $APP_SERVER_IP --dport 27017 -j ACCEPT iptables -A INPUT -p tcp --dport 27017 -j DROP netfilter-persistent save || true # ================================ # 7. Create Admin User # ================================ mongo admin --eval "db.createUser({ user: '$PUBLIC_PHRASE', pwd: '$PRIVATE_PHRASE', roles:[{ role:'root', db:'admin'}] })" || true # ================================ # 8. Backup Job # ================================ cat > /usr/local/bin/mongo_backup.sh <<EOF #!/bin/bash BACKUP_DIR="$BACKUP_PATH/mongo-\$(date +%F)" mongodump --out \$BACKUP_DIR --username $PUBLIC_PHRASE --password $PRIVATE_PHRASE --authenticationDatabase admin find $BACKUP_PATH -type d -mtime +$BACKUP_RETENTION_DAYS -exec rm -rf {} \; EOF chmod +x /usr/local/bin/mongo_backup.sh echo "0 2 * * 0 root /usr/local/bin/mongo_backup.sh" >> /etc/crontab echo "[✔] Setup complete. Config layered for flexibility." ``` --- ### 🔐 Security Notes - `CRED_FILE` (`mongo-cred.conf`) → should be `chmod 600`, root-only. - `SETTINGS_FILE` can be group-readable but best practice is also `600`. - `SOURCE_FILE` just points to locations, can be less strict. --- 👉 Would you like me to also add a **feature that encrypts the `PRIVATE_PHRASE`** in `mongo-cred.conf` (e.g., using `openssl` or `gpg`) and decrypts it at runtime, or keep it as plaintext but root-only readable?

Certificate authentication in MongoDB

user
is it possible to use certificate instead of password when running a mongodb server?
ChatGPT
Yes ✅, it’s possible to use **certificates instead of passwords** when running a MongoDB server. This is typically done using **x.509 certificate-based authentication**, which is supported in MongoDB Enterprise and Community editions. Here’s the breakdown: --- ### 🔑 How it Works - MongoDB can authenticate clients (users) and servers (in replica sets or sharded clusters) using **x.509 certificates** issued by a trusted Certificate Authority (CA). - Instead of providing a **username/password**, the client presents a certificate. - The server validates the certificate’s subject (DN) against an authorized user in MongoDB. --- ### ⚙️ Steps to Enable Certificate Authentication 1. **Generate Certificates** - Create a CA (or use an existing one). - Generate server and client certificates signed by the CA. - Ensure the certificates include the **Distinguished Name (DN)** in the `subject`. 2. **Configure MongoDB Server** ```yaml # mongod.conf net: ssl: mode: requireSSL PEMKeyFile: /etc/ssl/mongodb.pem CAFile: /etc/ssl/ca.pem security: authorization: enabled clusterAuthMode: x509 ``` Key options: - `mode: requireSSL` → All connections must use TLS/SSL. - `PEMKeyFile` → The server’s certificate + private key. - `CAFile` → The trusted CA file. - `clusterAuthMode: x509` → Required for certificate-based authentication. 3. **Create a MongoDB User with x.509** - Connect with an **admin client certificate**. - Create a user whose name matches the certificate’s DN: ```js use $external db.createUser({ user: "CN=myclient.example.com,OU=OrgUnit,O=MyOrg,L=City,ST=State,C=US", roles: [ { role: "readWrite", db: "test" } ] }) ``` > Notice: the `user` field must match the **subject DN** in the client’s certificate. 4. **Connect with a Client Using Certificate** ```bash mongo --host <hostname> \ --ssl \ --sslCAFile /etc/ssl/ca.pem \ --sslPEMKeyFile /etc/ssl/client.pem \ --authenticationMechanism MONGODB-X509 ``` --- ### 🔒 Benefits - No password leaks possible. - Stronger mutual authentication (both server and client validate each other). - Integrates with enterprise PKI setups. --- 👉 Would you like me to prepare a **step-by-step guide for Ubuntu** (certificate generation + MongoDB config + client connection), so you can test this locally?
user
can we use local certificates or letsencrypt for this?
ChatGPT
Great question 👍 Yes — you can use **either local self-signed certificates** or **public CA certificates like Let’s Encrypt** for MongoDB’s x.509 authentication, but with different implications: --- ## 🔹 1. Local / Self-Signed Certificates - Easiest for **development and internal/private networks**. - You generate your own **CA** and issue certs for the server and clients. - MongoDB trusts that CA (`CAFile` in config). - No internet validation required. - Downside: you need to securely distribute and trust your CA cert manually on every client machine. 👉 Best choice if you’re running MongoDB only inside a LAN, Docker, or a private cloud. --- ## 🔹 2. Let’s Encrypt Certificates - Let’s Encrypt is a **public CA** that issues free TLS certs for domain names. - You can use Let’s Encrypt for: - **Server TLS encryption** → MongoDB accepts secure connections with a valid, publicly trusted cert. - **Optional client authentication** if you generate **client certs signed by your own internal CA**, while still using Let’s Encrypt for the server cert. ⚠️ Important: - Let’s Encrypt certs are **valid only for servers with public DNS names** (not private IPs like `192.168.x.x` or `localhost`). - LE certificates expire every **90 days**, so you need to automate renewal (e.g., with `certbot`). 👉 This is useful if you want external clients to securely connect to MongoDB over the internet. --- ## 🔑 Typical Setup (Hybrid) - **Server cert**: Issued by Let’s Encrypt (so clients automatically trust the server). - **Client certs**: Issued by your own internal CA (so only your apps can authenticate). - MongoDB configured with both: ```yaml net: ssl: mode: requireSSL PEMKeyFile: /etc/letsencrypt/live/mongo.example.com/fullchain.pem PEMKeyPassword: <if any> CAFile: /etc/ssl/myCA.pem # your internal CA that signed client certs security: clusterAuthMode: x509 authorization: enabled ``` --- ✅ So: - For **local dev/testing** → use self-signed certs. - For **production / internet-facing MongoDB** → use Let’s Encrypt for the server, and your own CA for client certs. --- Do you want me to give you a **step-by-step Ubuntu guide** for setting up MongoDB with: 1. **Self-signed local CA certs** (private LAN use), and 2. **Let’s Encrypt certs** (internet-facing setup with `certbot`)?
user
Can you create a script to automate the number 2 solution.
ChatGPT
Got it 👍 — you want an **automation script for Ubuntu** that sets up **MongoDB with Let’s Encrypt (for the server cert)** and a **local CA (for client x.509 auth)**. Here’s a Bash script that: 1. Installs **certbot** and generates Let’s Encrypt certificates for your MongoDB domain. 2. Generates a **local CA** and issues **client certificates**. 3. Configures MongoDB to use the Let’s Encrypt server cert + internal CA for client authentication. 4. Sets up a **systemd service hook** to reload MongoDB when certs renew. --- ### 🔧 Script: `setup-mongo-cert-auth.sh` ```bash #!/bin/bash set -e # --- CONFIGURATION --- MONGO_DOMAIN="mongo.example.com" # Change to your public domain pointing to MongoDB MONGO_CONF="/etc/mongod.conf" LE_PATH="/etc/letsencrypt/live/$MONGO_DOMAIN" CA_DIR="/etc/mongoCA" CLIENT_DIR="/etc/mongoCA/clients" MONGO_USER="mongodb" # --- 1. Install dependencies --- echo "[*] Installing certbot and openssl..." apt-get update apt-get install -y certbot openssl # --- 2. Generate Let's Encrypt server certificate --- echo "[*] Generating Let's Encrypt certificate for $MONGO_DOMAIN ..." certbot certonly --standalone -d "$MONGO_DOMAIN" --non-interactive --agree-tos -m admin@"$MONGO_DOMAIN" # --- 3. Create a local CA for client certs --- echo "[*] Creating local CA..." mkdir -p "$CA_DIR" "$CLIENT_DIR" cd "$CA_DIR" if [ ! -f ca.key ]; then openssl genrsa -out ca.key 4096 openssl req -x509 -new -nodes -key ca.key -sha256 -days 3650 \ -subj "/C=US/ST=State/L=City/O=MyOrg/OU=DB/CN=MongoCA" \ -out ca.pem fi # --- 4. Generate client certificate (example client: app1) --- CLIENT_NAME="app1" echo "[*] Generating client certificate for $CLIENT_NAME..." openssl genrsa -out "$CLIENT_DIR/$CLIENT_NAME.key" 2048 openssl req -new -key "$CLIENT_DIR/$CLIENT_NAME.key" \ -subj "/C=US/ST=State/L=City/O=MyOrg/OU=Client/CN=$CLIENT_NAME" \ -out "$CLIENT_DIR/$CLIENT_NAME.csr" openssl x509 -req -in "$CLIENT_DIR/$CLIENT_NAME.csr" \ -CA ca.pem -CAkey ca.key -CAcreateserial \ -out "$CLIENT_DIR/$CLIENT_NAME.pem" -days 365 -sha256 # --- 5. Backup existing mongod.conf --- echo "[*] Backing up MongoDB config..." cp "$MONGO_CONF" "$MONGO_CONF.bak.$(date +%F-%H%M%S)" # --- 6. Configure MongoDB for SSL + x.509 --- echo "[*] Updating MongoDB config..." cat > "$MONGO_CONF" <<EOF net: ssl: mode: requireSSL PEMKeyFile: $LE_PATH/fullchain.pem PEMKeyPassword: "" CAFile: $CA_DIR/ca.pem security: clusterAuthMode: x509 authorization: enabled EOF # --- 7. Restart MongoDB --- echo "[*] Restarting MongoDB..." systemctl restart mongod # --- 8. Setup auto-reload on cert renew --- echo "[*] Setting up systemd reload hook for MongoDB..." cat > /etc/systemd/system/certbot-renew-mongo.service <<EOF [Unit] Description=Reload MongoDB after cert renewal [Service] Type=oneshot ExecStart=/bin/systemctl restart mongod EOF cat > /etc/systemd/system/certbot-renew-mongo.timer <<EOF [Unit] Description=Run MongoDB reload after certbot renew [Timer] OnCalendar=weekly Persistent=true [Install] WantedBy=timers.target EOF systemctl enable certbot-renew-mongo.timer systemctl start certbot-renew-mongo.timer echo "[✔] Setup complete!" echo "Client certificate for $CLIENT_NAME stored in $CLIENT_DIR" echo "Import this DN into MongoDB:" echo " use \$external" echo " db.createUser({user: 'CN=$CLIENT_NAME,OU=Client,O=MyOrg,L=City,ST=State,C=US', roles:[{role:'readWrite',db:'test'}]})" ``` --- ### 🔑 After Running the Script 1. Ensure `mongo.example.com` points to your server’s public IP. 2. MongoDB will now: - Use **Let’s Encrypt certs** for TLS. - Trust only clients with **your internal CA certs**. 3. Client connects with: ```bash mongo --host mongo.example.com \ --ssl \ --sslCAFile /etc/mongoCA/ca.pem \ --sslPEMKeyFile /etc/mongoCA/clients/app1.pem \ --authenticationMechanism MONGODB-X509 ``` --- ⚠️ Notes: - Replace `mongo.example.com` with your real domain. - Open MongoDB port (default `27017`) in firewall. - Let’s Encrypt requires **port 80** to be open temporarily during certificate issuance. --- 👉 Do you want me to also make a **companion script for generating new client certificates on demand** (e.g., `add-mongo-client.sh <clientName>`)? That way you don’t need to modify the main setup script again.
user
Instead of generating new client certificate on demand, can you modify the script to include monthly renewal of all certificates so that we can prevent issues when it expires. Can you also harden the certificate to expire within 3 months only.
ChatGPT
Perfect 👍 you’re thinking like a sysadmin: - **Let’s Encrypt certs** already expire every **90 days** (≈3 months). - We’ll **force client certificates** to also expire every **90 days** for consistency. - We’ll add a **monthly renewal job** that: 1. Runs `certbot renew` (for server cert). 2. Regenerates **all client certs** from your local CA with 90-day validity. 3. Restarts MongoDB so it reloads fresh certs. --- ## 🔧 Updated Script: `setup-mongo-cert-auth.sh` ```bash #!/bin/bash set -e # --- CONFIGURATION --- MONGO_DOMAIN="mongo.example.com" # Change this to your real domain MONGO_CONF="/etc/mongod.conf" LE_PATH="/etc/letsencrypt/live/$MONGO_DOMAIN" CA_DIR="/etc/mongoCA" CLIENT_DIR="/etc/mongoCA/clients" MONGO_USER="mongodb" # --- 1. Install dependencies --- echo "[*] Installing certbot and openssl..." apt-get update apt-get install -y certbot openssl # --- 2. Generate Let's Encrypt server certificate --- echo "[*] Generating Let's Encrypt certificate for $MONGO_DOMAIN ..." certbot certonly --standalone -d "$MONGO_DOMAIN" --non-interactive --agree-tos -m admin@"$MONGO_DOMAIN" # --- 3. Create local CA for client certs --- echo "[*] Creating local CA..." mkdir -p "$CA_DIR" "$CLIENT_DIR" cd "$CA_DIR" if [ ! -f ca.key ]; then openssl genrsa -out ca.key 4096 openssl req -x509 -new -nodes -key ca.key -sha256 -days 3650 \ -subj "/C=US/ST=State/L=City/O=MyOrg/OU=DB/CN=MongoCA" \ -out ca.pem fi # --- 4. Function: Generate client cert --- generate_client_cert() { local CLIENT_NAME=$1 echo "[*] Generating client certificate for $CLIENT_NAME..." openssl genrsa -out "$CLIENT_DIR/$CLIENT_NAME.key" 2048 openssl req -new -key "$CLIENT_DIR/$CLIENT_NAME.key" \ -subj "/C=US/ST=State/L=City/O=MyOrg/OU=Client/CN=$CLIENT_NAME" \ -out "$CLIENT_DIR/$CLIENT_NAME.csr" # Expire in 90 days max openssl x509 -req -in "$CLIENT_DIR/$CLIENT_NAME.csr" \ -CA ca.pem -CAkey ca.key -CAcreateserial \ -out "$CLIENT_DIR/$CLIENT_NAME.pem" -days 90 -sha256 } # --- 5. Generate default client cert (app1) --- generate_client_cert "app1" # --- 6. Backup existing mongod.conf --- echo "[*] Backing up MongoDB config..." cp "$MONGO_CONF" "$MONGO_CONF.bak.$(date +%F-%H%M%S)" || true # --- 7. Configure MongoDB for SSL + x.509 --- echo "[*] Updating MongoDB config..." cat > "$MONGO_CONF" <<EOF net: ssl: mode: requireSSL PEMKeyFile: $LE_PATH/fullchain.pem PEMKeyPassword: "" CAFile: $CA_DIR/ca.pem security: clusterAuthMode: x509 authorization: enabled EOF # --- 8. Restart MongoDB --- echo "[*] Restarting MongoDB..." systemctl restart mongod # --- 9. Create renewal script --- RENEW_SCRIPT="/usr/local/bin/mongo-cert-renew.sh" cat > "$RENEW_SCRIPT" <<'EOF' #!/bin/bash set -e MONGO_DOMAIN="mongo.example.com" LE_PATH="/etc/letsencrypt/live/$MONGO_DOMAIN" CA_DIR="/etc/mongoCA" CLIENT_DIR="/etc/mongoCA/clients" echo "[*] Renewing Let's Encrypt server certificate..." certbot renew --quiet echo "[*] Regenerating client certificates..." for csr in "$CLIENT_DIR"/*.csr; do CLIENT_NAME=$(basename "$csr" .csr) openssl x509 -req -in "$CLIENT_DIR/$CLIENT_NAME.csr" \ -CA "$CA_DIR/ca.pem" -CAkey "$CA_DIR/ca.key" -CAcreateserial \ -out "$CLIENT_DIR/$CLIENT_NAME.pem" -days 90 -sha256 done echo "[*] Restarting MongoDB to reload certs..." systemctl restart mongod EOF chmod +x "$RENEW_SCRIPT" # --- 10. Setup systemd timer for monthly renewal --- echo "[*] Setting up monthly renewal via systemd..." cat > /etc/systemd/system/mongo-cert-renew.service <<EOF [Unit] Description=Renew MongoDB certs (server + clients) [Service] Type=oneshot ExecStart=$RENEW_SCRIPT EOF cat > /etc/systemd/system/mongo-cert-renew.timer <<EOF [Unit] Description=Run MongoDB certificate renewal monthly [Timer] OnCalendar=monthly Persistent=true [Install] WantedBy=timers.target EOF systemctl enable mongo-cert-renew.timer systemctl start mongo-cert-renew.timer echo "[✔] Setup complete!" echo "Client certificate for app1 stored in $CLIENT_DIR" echo "Import this DN into MongoDB:" echo " use \$external" echo " db.createUser({user: 'CN=app1,OU=Client,O=MyOrg,L=City,ST=State,C=US', roles:[{role:'readWrite',db:'test'}]})" ``` --- ## 🔑 What This Script Does 1. **Let’s Encrypt server certs** → managed by `certbot`, auto-renewed every 90 days. 2. **Client certs** → regenerated monthly, each with **90-day expiry**. 3. **MongoDB** restarts automatically after renewals, so it always runs with fresh certs. --- ✅ This way, you’ll **never run into expired certs** unless MongoDB fails to restart. --- Would you like me to extend this so that when the **monthly renewal runs**, it also **emails you (or posts to Slack/Telegram)** if renewal fails, so you know before clients break?
user
Is it possible that when accessing the MongoDB via ssh or terminal we will use certificate but when accessing the database via mongoose or nodejs we will use username and password which can only be setup by admins who accessed via ssh or terminal and ensure that nobody can create additional users except those with access via certificate?
ChatGPT
Yes, that’s possible ✅ and it’s actually a **common pattern in production MongoDB setups**: - **Admins** authenticate with **x.509 certificates** (strong authentication, usually only accessible via SSH/terminal). - **Applications** (e.g., via Mongoose/Node.js) authenticate with **username/password** accounts created and controlled only by the certificate-authenticated admins. - You lock down the system so **only cert-authenticated users can create/manage new accounts**. --- ## 🔹 How to Enforce This ### 1. Enable x.509 for Admins - Configure MongoDB with **requireSSL + x.509 auth** (as in our script). - Create an **admin user** under `$external` authentication DB using the DN from the certificate: ```js use $external db.createUser({ user: "CN=AdminUser,OU=DB,O=MyOrg,L=City,ST=State,C=US", roles: [ { role: "userAdminAnyDatabase", db: "admin" }, { role: "dbAdminAnyDatabase", db: "admin" }, { role: "readWriteAnyDatabase", db: "admin" } ] }) ``` 🔑 This admin can only log in with the **certificate**. No password exists. --- ### 2. Create Application Accounts (Username/Password) - While logged in as the **cert-based admin**, create normal database users (for apps): ```js use myappdb db.createUser({ user: "appuser", pwd: "strongpassword123", roles: [ { role: "readWrite", db: "myappdb" } ] }) ``` - Apps connect via Mongoose like normal: ```js mongoose.connect("mongodb://appuser:strongpassword123@mongo.example.com:27017/myappdb?ssl=true"); ``` --- ### 3. Prevent Unauthorized User Creation - **Disable `createUser` and `grantRolesToUser` for password-based accounts** by using **role-based access control (RBAC)**. - By default, only users with `userAdmin` or `userAdminAnyDatabase` can create new accounts. - So, simply: **don’t give that role to any password-based user**. - Your password-based accounts (like `appuser`) should only have `readWrite` or `readWriteAnyDatabase`. --- ### 4. Enforce "Admin via Certificate Only" To make sure **only certificate-authenticated users can create/manage other users**: - In MongoDB: - The **only accounts with admin roles** (userAdmin, root, dbAdmin) must be created under `$external` with x.509 certs. - All **password accounts** are created with minimal roles (`readWrite`, `read`) only. - In practice: ```js // ❌ never do this for a password account db.grantRolesToUser("appuser", [{ role: "userAdminAnyDatabase", db: "admin" }]) ``` - If someone compromises a password user, they **cannot escalate** because they don’t have `userAdmin` roles. --- ### 🔒 Security Flow Recap - **SSH/Terminal (Admins)** → login with MongoDB **cert-based x.509** → can create/modify users. - **Applications (Node.js/Mongoose)** → login with **username/password** → limited DB access only. - **Separation of duties** → Only cert-authenticated admins manage accounts. No password-based user can create another user. --- ✅ This is fully supported by MongoDB’s **RBAC model** and x.509 authentication. It’s also aligned with security best practices: - **Admins use certificates** (no passwords to leak). - **Apps use passwords** (easy integration, limited scope). --- Would you like me to **extend the automation script** we made so it: 1. Creates a **cert-only admin user** (for SSH/terminal). 2. Blocks any password account from having admin rights. 3. Provides a helper command for admins to create new app accounts?
user
We are currently using mongodb 3.4.24 can you make these solutions automated that we can only run in one script?
ChatGPT
Thanks for clarifying 👍. Since you’re on **MongoDB 3.4.24**, there are a few important points: - **x.509 authentication** is already supported in 3.4 (introduced in 2.6). - RBAC (role-based access control) is also supported. - MongoDB 3.4 is **end-of-life (EOL)** — so no more patches, but your requirements are still doable. - The configs are slightly different (SSL options instead of `net.tls` used in newer versions). --- ## 🎯 What the “One Script” Should Do When you run it once on Ubuntu, it should: 1. Install MongoDB dependencies (skip if already installed). 2. Generate a Let’s Encrypt cert for the **server**. 3. Generate a **local CA + client certs** for **admins**. 4. Configure MongoDB (`mongod.conf`) to: - Require SSL, - Accept Let’s Encrypt server cert, - Trust local CA for client x.509. 5. Start MongoDB. 6. Create an **admin user authenticated by certificate only**. 7. Configure roles so **only that admin** can create new users. 8. Create an **example app user (username/password)** with limited rights. 9. Set up **monthly renewal** of server + client certs. --- ## 🔧 Automated Script for MongoDB 3.4.24 Save as: `setup-mongo34-secure.sh` ```bash #!/bin/bash set -e # --- CONFIGURATION --- MONGO_DOMAIN="mongo.example.com" # <-- Change this to your public domain MONGO_CONF="/etc/mongod.conf" LE_PATH="/etc/letsencrypt/live/$MONGO_DOMAIN" CA_DIR="/etc/mongoCA" CLIENT_DIR="/etc/mongoCA/clients" ADMIN_CN="MongoAdmin" # CN for admin cert APP_USER="appuser" APP_PASS="StrongPassword123" # --- 1. Install dependencies --- echo "[*] Installing certbot and openssl..." apt-get update apt-get install -y certbot openssl # --- 2. Get Let's Encrypt server certificate --- echo "[*] Requesting Let's Encrypt certificate for $MONGO_DOMAIN ..." certbot certonly --standalone -d "$MONGO_DOMAIN" --non-interactive --agree-tos -m admin@"$MONGO_DOMAIN" # --- 3. Create local CA --- echo "[*] Creating local CA..." mkdir -p "$CA_DIR" "$CLIENT_DIR" cd "$CA_DIR" if [ ! -f ca.key ]; then openssl genrsa -out ca.key 4096 openssl req -x509 -new -nodes -key ca.key -sha256 -days 3650 \ -subj "/C=US/ST=State/L=City/O=MyOrg/OU=DB/CN=MongoCA" \ -out ca.pem fi # --- 4. Generate admin client cert (90 days) --- echo "[*] Generating admin client certificate..." openssl genrsa -out "$CLIENT_DIR/admin.key" 2048 openssl req -new -key "$CLIENT_DIR/admin.key" \ -subj "/C=US/ST=State/L=City/O=MyOrg/OU=DB/CN=$ADMIN_CN" \ -out "$CLIENT_DIR/admin.csr" openssl x509 -req -in "$CLIENT_DIR/admin.csr" \ -CA ca.pem -CAkey ca.key -CAcreateserial \ -out "$CLIENT_DIR/admin.pem" -days 90 -sha256 # --- 5. Backup mongod.conf --- echo "[*] Backing up mongod.conf..." cp "$MONGO_CONF" "$MONGO_CONF.bak.$(date +%F-%H%M%S)" || true # --- 6. Configure MongoDB for SSL --- echo "[*] Updating MongoDB config..." cat > "$MONGO_CONF" <<EOF net: ssl: mode: requireSSL PEMKeyFile: $LE_PATH/fullchain.pem PEMKeyPassword: "" CAFile: $CA_DIR/ca.pem security: clusterAuthMode: x509 authorization: enabled EOF # --- 7. Restart MongoDB --- echo "[*] Restarting MongoDB..." systemctl restart mongod # --- 8. Create Admin User (x.509 only) --- echo "[*] Creating x.509 admin user..." mongo --ssl \ --sslCAFile "$CA_DIR/ca.pem" \ --sslPEMKeyFile "$CLIENT_DIR/admin.pem" \ --host "$MONGO_DOMAIN" \ --eval "db.getSiblingDB('\$external').createUser({ user: 'CN=$ADMIN_CN,OU=DB,O=MyOrg,L=City,ST=State,C=US', roles: [ {role:'userAdminAnyDatabase', db:'admin'}, {role:'dbAdminAnyDatabase', db:'admin'}, {role:'readWriteAnyDatabase', db:'admin'} ] })" # --- 9. Create App User (username/password, no admin rights) --- echo "[*] Creating restricted app user..." mongo --ssl \ --sslCAFile "$CA_DIR/ca.pem" \ --sslPEMKeyFile "$CLIENT_DIR/admin.pem" \ --host "$MONGO_DOMAIN" \ --eval "db.getSiblingDB('myappdb').createUser({ user: '$APP_USER', pwd: '$APP_PASS', roles: [{role:'readWrite', db:'myappdb'}] })" # --- 10. Create monthly renewal script --- echo "[*] Setting up monthly renewal..." RENEW_SCRIPT="/usr/local/bin/mongo-cert-renew.sh" cat > "$RENEW_SCRIPT" <<'EOF' #!/bin/bash set -e MONGO_DOMAIN="mongo.example.com" LE_PATH="/etc/letsencrypt/live/$MONGO_DOMAIN" CA_DIR="/etc/mongoCA" CLIENT_DIR="/etc/mongoCA/clients" echo "[*] Renewing Let's Encrypt cert..." certbot renew --quiet echo "[*] Renewing admin cert..." openssl genrsa -out "$CLIENT_DIR/admin.key" 2048 openssl req -new -key "$CLIENT_DIR/admin.key" \ -subj "/C=US/ST=State/L=City/O=MyOrg/OU=DB/CN=MongoAdmin" \ -out "$CLIENT_DIR/admin.csr" openssl x509 -req -in "$CLIENT_DIR/admin.csr" \ -CA "$CA_DIR/ca.pem" -CAkey "$CA_DIR/ca.key" -CAcreateserial \ -out "$CLIENT_DIR/admin.pem" -days 90 -sha256 echo "[*] Restarting MongoDB..." systemctl restart mongod EOF chmod +x "$RENEW_SCRIPT" cat > /etc/systemd/system/mongo-cert-renew.service <<EOF [Unit] Description=Renew MongoDB certs [Service] Type=oneshot ExecStart=$RENEW_SCRIPT EOF cat > /etc/systemd/system/mongo-cert-renew.timer <<EOF [Unit] Description=Run MongoDB cert renew monthly [Timer] OnCalendar=monthly Persistent=true [Install] WantedBy=timers.target EOF systemctl enable mongo-cert-renew.timer systemctl start mongo-cert-renew.timer echo "[✔] Setup complete!" echo "Admin cert: $CLIENT_DIR/admin.pem" echo "App user: $APP_USER / $APP_PASS" echo "Only x.509 admin can create new users." ``` --- ## 🔑 What This Script Achieves - **Server cert** = Let’s Encrypt (auto-renew every 90 days). - **Admin cert** = Local CA, expires every 90 days, renewed monthly. - **Admin account** = x.509 only, with full privileges. - **App account** = username/password, `readWrite` only (no user creation). - **Monthly renewal** keeps everything fresh. --- ⚠️ Important: - MongoDB 3.4 still uses `net.ssl.*` config keys, not `net.tls.*`. I adjusted for that. - Admin DN must match exactly with the cert CN (`CN=MongoAdmin,...`). - If renewal generates a new admin cert, you’ll need to reconnect with the updated file. --- 👉 Do you want me to also extend this so that **when the admin cert is renewed**, the script automatically **replaces the old user with the new DN** in MongoDB (so you don’t have to run manual cleanup)?
user
Yes please, modify the script such that when certificate is renewed, all old users will be renewed also and it should not affect any applications connecting to the server via username and password
ChatGPT
Perfect 👍 I understand — you want **zero downtime for apps using username/password**, while still rotating all **x.509 admin certs** every 90 days. Here’s how we’ll handle it: - **Password-based users** (like `appuser`) → unchanged. Their accounts are not touched by renewal. - **Certificate-based users (admins)** → reissued every 90 days. - On renewal: 1. Generate a fresh admin certificate. 2. Extract the **new DN** from the cert. 3. Remove old x.509 admin users. 4. Re-create them with the **new DN**. 5. Restart MongoDB to apply. This way: - Apps keep working with username/password. - Admins keep working, but only need to use the **new cert** file (renewed monthly). --- ## 🔧 Modified Script for MongoDB 3.4.24 Save as `setup-mongo34-secure.sh` (replaces the previous one). ```bash #!/bin/bash set -e # --- CONFIGURATION --- MONGO_DOMAIN="mongo.example.com" # <-- Change this to your real domain MONGO_CONF="/etc/mongod.conf" LE_PATH="/etc/letsencrypt/live/$MONGO_DOMAIN" CA_DIR="/etc/mongoCA" CLIENT_DIR="/etc/mongoCA/clients" ADMIN_CN="MongoAdmin" # CN for admin cert APP_USER="appuser" APP_PASS="StrongPassword123" # --- 1. Install dependencies --- echo "[*] Installing certbot and openssl..." apt-get update apt-get install -y certbot openssl jq # --- 2. Get Let's Encrypt server certificate --- echo "[*] Requesting Let's Encrypt certificate for $MONGO_DOMAIN ..." certbot certonly --standalone -d "$MONGO_DOMAIN" --non-interactive --agree-tos -m admin@"$MONGO_DOMAIN" # --- 3. Create local CA --- echo "[*] Creating local CA..." mkdir -p "$CA_DIR" "$CLIENT_DIR" cd "$CA_DIR" if [ ! -f ca.key ]; then openssl genrsa -out ca.key 4096 openssl req -x509 -new -nodes -key ca.key -sha256 -days 3650 \ -subj "/C=US/ST=State/L=City/O=MyOrg/OU=DB/CN=MongoCA" \ -out ca.pem fi # --- 4. Function to generate admin cert and add to MongoDB --- generate_admin_cert() { local ADMIN_NAME=$1 echo "[*] Generating admin client certificate ($ADMIN_NAME)..." openssl genrsa -out "$CLIENT_DIR/$ADMIN_NAME.key" 2048 openssl req -new -key "$CLIENT_DIR/$ADMIN_NAME.key" \ -subj "/C=US/ST=State/L=City/O=MyOrg/OU=DB/CN=$ADMIN_NAME" \ -out "$CLIENT_DIR/$ADMIN_NAME.csr" openssl x509 -req -in "$CLIENT_DIR/$ADMIN_NAME.csr" \ -CA ca.pem -CAkey ca.key -CAcreateserial \ -out "$CLIENT_DIR/$ADMIN_NAME.pem" -days 90 -sha256 local DN="CN=$ADMIN_NAME,OU=DB,O=MyOrg,L=City,ST=State,C=US" echo "[*] Creating MongoDB admin user for DN: $DN" mongo --ssl \ --sslCAFile "$CA_DIR/ca.pem" \ --sslPEMKeyFile "$CLIENT_DIR/$ADMIN_NAME.pem" \ --host "$MONGO_DOMAIN" \ --eval "db.getSiblingDB('\$external').createUser({ user: '$DN', roles: [ {role:'userAdminAnyDatabase', db:'admin'}, {role:'dbAdminAnyDatabase', db:'admin'}, {role:'readWriteAnyDatabase', db:'admin'} ] })" } # --- 5. Backup mongod.conf --- echo "[*] Backing up mongod.conf..." cp "$MONGO_CONF" "$MONGO_CONF.bak.$(date +%F-%H%M%S)" || true # --- 6. Configure MongoDB for SSL --- echo "[*] Updating MongoDB config..." cat > "$MONGO_CONF" <<EOF net: ssl: mode: requireSSL PEMKeyFile: $LE_PATH/fullchain.pem PEMKeyPassword: "" CAFile: $CA_DIR/ca.pem security: clusterAuthMode: x509 authorization: enabled EOF # --- 7. Restart MongoDB --- echo "[*] Restarting MongoDB..." systemctl restart mongod # --- 8. Initial admin user --- generate_admin_cert "$ADMIN_CN" # --- 9. Create App User (username/password, no admin rights) --- echo "[*] Creating restricted app user..." mongo --ssl \ --sslCAFile "$CA_DIR/ca.pem" \ --sslPEMKeyFile "$CLIENT_DIR/$ADMIN_CN.pem" \ --host "$MONGO_DOMAIN" \ --eval "db.getSiblingDB('myappdb').createUser({ user: '$APP_USER', pwd: '$APP_PASS', roles: [{role:'readWrite', db:'myappdb'}] })" # --- 10. Renewal script --- echo "[*] Creating renewal script..." RENEW_SCRIPT="/usr/local/bin/mongo-cert-renew.sh" cat > "$RENEW_SCRIPT" <<'EOF' #!/bin/bash set -e MONGO_DOMAIN="mongo.example.com" CA_DIR="/etc/mongoCA" CLIENT_DIR="/etc/mongoCA/clients" ADMIN_CN="MongoAdmin" echo "[*] Renewing Let's Encrypt server cert..." certbot renew --quiet echo "[*] Generating new admin cert..." NEW_NAME="$ADMIN_CN-$(date +%Y%m%d)" openssl genrsa -out "$CLIENT_DIR/$NEW_NAME.key" 2048 openssl req -new -key "$CLIENT_DIR/$NEW_NAME.key" \ -subj "/C=US/ST=State/L=City/O=MyOrg/OU=DB/CN=$NEW_NAME" \ -out "$CLIENT_DIR/$NEW_NAME.csr" openssl x509 -req -in "$CLIENT_DIR/$NEW_NAME.csr" \ -CA "$CA_DIR/ca.pem" -CAkey "$CA_DIR/ca.key" -CAcreateserial \ -out "$CLIENT_DIR/$NEW_NAME.pem" -days 90 -sha256 NEW_DN="CN=$NEW_NAME,OU=DB,O=MyOrg,L=City,ST=State,C=US" echo "[*] Adding new admin user with DN $NEW_DN..." mongo --ssl \ --sslCAFile "$CA_DIR/ca.pem" \ --sslPEMKeyFile "$CLIENT_DIR/$NEW_NAME.pem" \ --host "$MONGO_DOMAIN" \ --eval "db.getSiblingDB('\$external').createUser({ user: '$NEW_DN', roles: [ {role:'userAdminAnyDatabase', db:'admin'}, {role:'dbAdminAnyDatabase', db:'admin'}, {role:'readWriteAnyDatabase', db:'admin'} ] })" echo "[*] Removing old admin users..." mongo --ssl \ --sslCAFile "$CA_DIR/ca.pem" \ --sslPEMKeyFile "$CLIENT_DIR/$NEW_NAME.pem" \ --host "$MONGO_DOMAIN" \ --eval 'db.getSiblingDB("$external").getUsers().forEach(function(u){ if(u.user.indexOf("CN=MongoAdmin")===0 && u.user!="'$NEW_DN'"){ db.dropUser(u.user); } });' echo "[*] Restarting MongoDB..." systemctl restart mongod echo "[✔] Renewal complete. Active admin cert: $CLIENT_DIR/$NEW_NAME.pem" EOF chmod +x "$RENEW_SCRIPT" # --- 11. Setup systemd monthly timer --- cat > /etc/systemd/system/mongo-cert-renew.service <<EOF [Unit] Description=Renew MongoDB certs [Service] Type=oneshot ExecStart=$RENEW_SCRIPT EOF cat > /etc/systemd/system/mongo-cert-renew.timer <<EOF [Unit] Description=Run MongoDB cert renew monthly [Timer] OnCalendar=monthly Persistent=true [Install] WantedBy=timers.target EOF systemctl enable mongo-cert-renew.timer systemctl start mongo-cert-renew.timer echo "[✔] Setup complete!" echo "➡ Admin cert: $CLIENT_DIR/${ADMIN_CN}.pem" echo "➡ App user: $APP_USER / $APP_PASS" echo "➡ Admin certs rotate every 90 days; old cert users are automatically deleted." echo "➡ App password users remain unaffected." ``` --- ## 🔑 How This Works - **Initial run**: - Creates an admin cert (`CN=MongoAdmin`). - Adds admin x.509 user in MongoDB. - Creates app user (username/password). - **On monthly renewal**: - Let’s Encrypt server cert renewed. - New admin cert generated (`CN=MongoAdmin-YYYYMMDD`). - Added as admin in MongoDB. - Old admin users matching `CN=MongoAdmin*` are dropped. - MongoDB restarted. - App user (`appuser`) is **untouched**. --- ✅ Result: - **Only admins with fresh certs** can create new users. - **Apps keep running** without interruption using username/password. - **No risk of expired certs** because rotation + cleanup is automatic. --- Do you want me to also add an **email/Slack alert** in the renewal script so you’re notified whenever a new admin cert is issued and the old one is dropped?